﻿using System;
using System.Collections.Generic;

using System.Text;
using System.Drawing;

namespace Firefly.Controls.Extentions {
    public class TerminalParser {
        /// <summary>
        /// Take unmodified text from the socket and format it into something useful.
        /// </summary>
        /// <param name="s"></param>
        /// <returns></returns>
        public TextFragment[] Parse(string s) {
            List<TextFragment> parsedText = new List<TextFragment>();
            TextFragment textTemp = new TextFragment() { Ansi = ansiState.Clone() };

            foreach (char c in s) {
                if (c == (char)0x1B) { // Start ANSI
                    parsingAnsi = true;
                } else if (c == '[' && parsingAnsi) { // Does nothing                    
                } else if (parsingAnsi && c != 'm' && c != ';') { // The meat! Add ansi codes to data.
                    ansiCode += c;
                } else if (c == ';' && parsingAnsi) { // Code separator
                    // Update Ansi State
                    ParseAnsi(Convert.ToInt32(ansiCode));
                    // Reset
                    ansiCode = String.Empty;
                } else if (c == 'm' && parsingAnsi) { // End ANSI
                    parsingAnsi = false;
                    // Update Ansi State
                    ParseAnsi(Convert.ToInt32(ansiCode));
                    // Reset
                    ansiCode = String.Empty;
                    // Ansi code change. New text fragment needed.
                    parsedText.Add(textTemp);
                    textTemp = new TextFragment() { Ansi = ansiState.Clone() };
                } else {
                    mCurrentCharWidth++;
                    // Add text to the text fragment using the last ansi code.
                    textTemp.Text += c;
                    if (c == '\n')
                        mCurrentCharWidth = 0;
                    if (mCurrentCharWidth > mTerminalWidth) {
                        //textTemp.Text += '\n';
                        //mCurrentCharWidth = 0;
                    }
                }
            }
            parsedText.Add(textTemp);
            return parsedText.ToArray();
        }

        private void ParseAnsi(int code) {
            Console.WriteLine("Found ANSI code: " + code);
            if (code > 29 && code < 38) {
                ansiState.Primary = code;
            } else if (code == 4) {
                Console.WriteLine("Enable underline code!");
                ansiState.Underline = true;
            } else if (code == 5) {
                ansiState.Blink = true;
            } else if (code == 1) {
                ansiState.PrimaryBright = true;
            } else if (code == 0) {
                ansiState.Primary = 0;
                ansiState.PrimaryBright = false;
                ansiState.Underline = false;
                ansiState.Blink = false;
            }
        }

        private bool parsingAnsi = false;
        private AnsiCode ansiState = new AnsiCode() { Primary = 0, PrimaryBright = false, Secondary = 30 };
        private string ansiCode = String.Empty;
        public int mTerminalWidth = 78;
        private int mCurrentCharWidth;
    }


    public struct TextFragment {
        public string Text { get; set; }
        public bool NewLine { get; set; }
        public AnsiCode Ansi { get; set; }
    }

    public class AnsiCode {
        public int Primary { get; set; } // Main Color
        public int Secondary { get; set; } // Back Color
        public bool PrimaryBright { get; set; } // Primary high color
        public bool SecondaryBright { get; set; } // Secondary High Color
        public bool Underline { get; set; }
        public bool Blink { get; set; }

        public Color PrimaryColor {
            get {
                return (PrimaryBright) ? ColorsBright[Primary] : ColorsNorm[Primary];
            }
        }

        public Color SecondaryColor {
            get {
                return (SecondaryBright) ? ColorsBright[Secondary] : ColorsNorm[(Secondary == 0) ? 30 : Secondary];
            }
        }

        public AnsiCode Clone() {
            return (AnsiCode)MemberwiseClone();
        }

        public static Dictionary<int, Color> ColorsNorm = new Dictionary<int, Color>() {
            { 0, Color.WhiteSmoke },
            { 30, Color.FromArgb( 0, 0, 0) },
            { 31, Color.FromArgb( 187, 0, 0) },
            { 32, Color.FromArgb( 0, 187, 0) },
            { 33, Color.FromArgb( 187, 187, 0) },
            { 34, Color.FromArgb( 0, 0, 187) },
            { 35, Color.FromArgb( 187, 0, 187) },
            { 36, Color.FromArgb( 0, 187, 187) },
            { 37, Color.FromArgb( 187, 187, 187) }
        };
        public static Dictionary<int, Color> ColorsBright = new Dictionary<int, Color>() {
            { 0, Color.WhiteSmoke },
            { 30, Color.FromArgb( 127, 127, 127) },
            { 31, Color.FromArgb( 255, 0, 0) },
            { 32, Color.FromArgb( 0, 255, 0) },
            { 33, Color.FromArgb( 255, 255, 0) },
            { 34, Color.FromArgb( 92, 92, 255) },
            { 35, Color.FromArgb( 255, 0, 255) },
            { 36, Color.FromArgb( 0, 255, 255) },
            { 37, Color.FromArgb( 255, 255, 255) },
        };
    }
}
